package com.gzhu.knowledgeAdmin.service.impl;

import cn.hutool.core.io.file.FileReader;
import cn.hutool.core.io.resource.InputStreamResource;
import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.gzhu.knowledgeAdmin.common.constant.OperatorType;
import com.gzhu.knowledgeAdmin.common.exception.BusinessException;
import com.gzhu.knowledgeAdmin.common.param.TaskParam;
import com.gzhu.knowledgeAdmin.common.result.ErrorCode;
import com.gzhu.knowledgeAdmin.common.utils.GoFastDfsUtil;
import com.gzhu.knowledgeAdmin.common.utils.UserInfoUtils;
import com.gzhu.knowledgeAdmin.common.vo.*;
import com.gzhu.knowledgeAdmin.entity.*;
import com.gzhu.knowledgeAdmin.service.*;
import io.swagger.models.auth.In;
import org.apache.commons.jexl3.JexlBuilder;
import org.apache.commons.jexl3.JexlEngine;
import org.apache.commons.jexl3.JexlExpression;
import org.apache.commons.jexl3.MapContext;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.regex.PatternSyntaxException;
import java.util.stream.Collectors;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;


import com.gzhu.knowledgeAdmin.dao.RuleDao;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;
import org.springframework.web.multipart.MultipartFile;


@Service("ruleService")
public class RuleServiceImpl extends ServiceImpl<RuleDao, Rule> implements RuleService {

    @Autowired
    private ConditionService conditionService;

    @Autowired
    private OperatorService operatorService;

    @Autowired
    private KeywordService keywordService;

    @Autowired
    private TRwjgjfTService tRwjgjfTService;


    @Autowired
    private GoFastDfsUtil goFastDfsUtil;

    @Autowired
    private CheckResultService checkResultService;

    @Override
    public PageVo queryPage(Map<String, Object> params) {
        // 获取当前页和每页大小
        long pageNo = Long.parseLong((String) params.get("page"));
        long pageSize = Long.parseLong((String) params.get("limit"));
        String keyWord = (String) params.get("keywords");
        String sceneIdStr = (String) params.get("sceneId");


        Page<RuleVo> page = new Page<>(pageNo, pageSize);
        QueryWrapper<RuleVo> wrapper = new QueryWrapper<>();

        if (!StringUtils.isEmpty(keyWord)) {
            wrapper.like("r.name", keyWord);
        }
        if (StringUtils.hasLength(sceneIdStr)) {
            int sceneId = Integer.parseInt(sceneIdStr);
            wrapper.eq("r.scene_id", sceneId);
        }
        // 只能查询自己公司的
        String gsid = UserInfoUtils.getCurrentUser().getGsid();
        wrapper.eq("r.gsid", gsid);


        page = baseMapper.selectRulePage(page, wrapper);

        return new PageVo(page);
    }

    @Override
    public void saveRule(Rule rule) {
        // 获取公司id
        String gsid = UserInfoUtils.getCurrentUser().getGsid();
        rule.setGsid(gsid);
        save(rule);
    }

    @Override
    @Transactional
    public void removeRulesByIds(List<Integer> ids) {

        // 判断规则是否被引用
        Set<Integer> ruleIdSet = new HashSet<>(ids);
        checkIfUsed(ruleIdSet);

        // 删除涉及到的所有条件
        Collection<Rule> rules = listByIds(ids);
        HashSet<Integer> conditionIds = new HashSet<>();
        for (Rule rule : rules) {
            conditionIds.addAll(com.gzhu.knowledgeAdmin.common.utils.StringUtils.extractNumbers(rule.getContent()));
        }

        conditionService.removeConditionByIds(new ArrayList<>(conditionIds));


        // 删除条件
        removeByIds(ids);


    }

    private void checkIfUsed(Set<Integer> ruleIdSet) {
        // 获取公司id
        String gsid = UserInfoUtils.getCurrentUser().getGsid();

        // 获取所有检测记录
        QueryWrapper<CheckResult> wrapper = new QueryWrapper<>();
        wrapper.eq("gsid",gsid);
        List<CheckResult> checkResultList = checkResultService.list(wrapper);

        for (CheckResult checkResult : checkResultList) {
            String ruleIds = checkResult.getRuleIds();
            String[] split = ruleIds.split("\\|");
            for (String s : split) {
                if(ruleIdSet.contains(Integer.parseInt(s))){
                    throw new BusinessException(ErrorCode.USED_ERROR);
                }
            }
        }

    }


    @Override
    public RuleDetailVo getRuleById(Integer id) {

        Rule rule = baseMapper.selectRuleById(id);

        /**
         * 设置条件列表
         *
         * 1. 查找涉及到的所有条件
         * 2. 将规则中的content的id替换成序号
         * 3. 封装结果
         */
        // 涉及到的所有条件
        List<Integer> conditionIds = com.gzhu.knowledgeAdmin.common.utils.StringUtils.extractNumbers(rule.getContent());
        Collection<Condition> conditions = conditionService.listByIds(conditionIds);
        ArrayList<Condition> conditionArrayList = new ArrayList<>(conditions);

        // 将规则中的content的id替换成序号
        Map<Integer, Integer> map = conditionArrayList.stream().collect(Collectors.toMap(Condition::getId, condition -> conditionArrayList.indexOf(condition) + 1));
        String s = com.gzhu.knowledgeAdmin.common.utils.StringUtils.replaceIds(rule.getContent(), map);
        rule.setContent(s);

        // 封装结果
        RuleDetailVo ruleDetailVo = new RuleDetailVo();
        BeanUtils.copyProperties(rule, ruleDetailVo);

        // 封装conditionList
        List<ConditionVo> conditionVoList = conditionArrayList.stream().map(item ->
                conditionService.getConditionById(item.getId())
        ).collect(Collectors.toList());

        ruleDetailVo.setConditionList(conditionVoList);

        return ruleDetailVo;
    }


    /**
     * 使用规则进行检测字符串
     *
     * @param str    待检测的字符串
     * @param ruleDetailVo 使用的规则
     * @return 返回处理后的字符串, 如果不匹配，则返回空
     */
    private String checkString(String str, RuleDetailVo ruleDetailVo) {

        // ==========================================检测文本，高亮显示===============
        // 检测
        Boolean res = check(ruleDetailVo, str);


        // 获取所有涉及到的算子
        HashSet<String> stringHashSet = new HashSet<>(); // 避免重复高亮显示
        for (ConditionVo conditionVo : ruleDetailVo.getConditionList()) {
            for (Operator operator : conditionVo.getOperatorList()) {
                stringHashSet.add(operator.getContent());
            }
        }

        // 高亮显示匹配到的关键词
        String highlightMatches = highlightMatches(str, stringHashSet);

        return res ? highlightMatches : null;

    }

    private RuleDetailVo getRuleDetailVo(Integer ruleId) {
        // 获取规则
        Rule rule = baseMapper.selectById(ruleId);

        // 提取涉及到的所有条件id
        List<Integer> conditionIdList = com.gzhu.knowledgeAdmin.common.utils.StringUtils.extractNumbers(rule.getContent());

        // 获取规则的条件列表
        Collection<Condition> conditionCollection = conditionService.listByIds(conditionIdList);
        List<Condition> conditionList = new ArrayList<>(conditionCollection);

        // 封装条件
        List<ConditionVo> conditionVoList = conditionList.stream().map(condition -> {
            // 提取涉及到的所有算子id
            List<Integer> operatorIdList = com.gzhu.knowledgeAdmin.common.utils.StringUtils.extractNumbers(condition.getContent());

            // 设置算子列表
            Collection<Operator> operatorCollection = operatorService.listByIds(operatorIdList);
            ArrayList<Operator> operatorList = new ArrayList<>(operatorCollection);

            /**
             * 如果为包含关键字类型的算子，那么要将关键词id替换成关键词名称
             */
            // 1.获取所有涉及到的关键词，并封装成一个map<id,关键词>
            HashSet<Integer> keywordIdsSet = new HashSet<>();
            for (Operator operator : operatorList) {
                if (operator.getType() == OperatorType.CONTAIN_KEYWORD) {
                    String[] split = operator.getContent().split(",");
                    for (String s : split) {
                        keywordIdsSet.add(Integer.parseInt(s));
                    }
                }
            }

            // 2.存在再进行替换操作
            if (!keywordIdsSet.isEmpty()) {

                Collection<Keyword> keywords = keywordService.listByIds(keywordIdsSet);
                Map<Integer, String> keywordMap = keywords.stream().collect(Collectors.toMap(Keyword::getId, Keyword::getName));
                // 替换算子中关键词类型记录的content的id为对称应名
                for (Operator operator : operatorList) {
                    if (operator.getType() == OperatorType.CONTAIN_KEYWORD) {

                        String operatorContent = operator.getContent();
                        String[] split = operatorContent.split(",");

                        StringBuilder stringBuilder = new StringBuilder();
                        for (String keywordId : split) {
                            Integer id = Integer.parseInt(keywordId);
                            stringBuilder.append(keywordMap.getOrDefault(id, keywordId));
                            stringBuilder.append("|");
                        }
                        // 移除最后一个多余的逗号
                        if (stringBuilder.length() > 0) {
                            stringBuilder.deleteCharAt(stringBuilder.length() - 1);
                        }
                        operator.setContent(stringBuilder.toString());
                    }
                }

            }


            // 封装条件对象
            ConditionVo conditionVo = new ConditionVo();
            BeanUtils.copyProperties(condition, conditionVo);
            conditionVo.setOperatorList(operatorList);
            return conditionVo;
        }).collect(Collectors.toList());


        // 封装规则对象
        RuleDetailVo ruleDetailVo = new RuleDetailVo();
        BeanUtils.copyProperties(rule, ruleDetailVo);
        ruleDetailVo.setConditionList(conditionVoList);
        return ruleDetailVo;
    }

    /**
     * 批量进行检测
     * @param taskParam
     */
    @Override
    @Transactional
    public void checkBatch(TaskParam taskParam) throws Exception {

        // 获取数据集
        List<TRwjgjfT> tRwjgjfTList = (List<TRwjgjfT>) tRwjgjfTService.listByIds(taskParam.getSelectedDataSetIds());


        // 准备好所有待执行的规则
        List<Integer> ruleIds = taskParam.getSelectedRuleIds();

        StringBuilder stringBuilder = new StringBuilder();
        for (Integer ruleId : ruleIds) {
            stringBuilder.append(ruleId).append('|');
        }
        stringBuilder.deleteCharAt(stringBuilder.length()-1);
        String ruleIdsStr = stringBuilder.toString();


        ArrayList<RuleDetailVo> ruleList = new ArrayList<>();
        for (Integer ruleId : ruleIds) {
            ruleList.add(getRuleDetailVo(ruleId));
        }

        // 检测
        String gsid = UserInfoUtils.getCurrentUser().getGsid();

        int passCount = 0;
        for (TRwjgjfT tRwjgjfT : tRwjgjfTList) {

            // 获取待检测文件
            String filePath = tRwjgjfT.getFRwnrwz();
            String text = new String(goFastDfsUtil.getFileBytes(filePath));
/*            FileReader fileReader = new FileReader(filePath);
            fileReader.getFile().getName();
            String text = fileReader.readString();*/

            // 将json文件转化为List数组
            ObjectMapper objectMapper = new ObjectMapper();
            CheckVo checkVo = objectMapper.readValue(text, new TypeReference<CheckVo>() {
            });
            List<CheckRecordVo> record = checkVo.getRecord();

            // 记录规则匹配情况
            HashMap<Integer, Boolean> ruleMatchMap = new HashMap<>();
            for (RuleDetailVo rule : ruleList){
                ruleMatchMap.put(rule.getId(),false);
            }

            // 逐条检查文本
            for(CheckRecordVo entry: record){

                String curText = entry.getContent();
                for (RuleDetailVo rule : ruleList) {
                    String s = checkString(curText, rule);
                    if (s != null) {
                        ruleMatchMap.put(rule.getId(),true);
                        curText = s;
                    }
                }
                // 覆盖原来的文本
                entry.setContent(curText);
            }

            // 计算匹配条数
            for(Map.Entry<Integer,Boolean> entry : ruleMatchMap.entrySet()){
                if(entry.getValue()){
                    passCount++;
                }
            }



            // 保存检测后的文本文件到文件服务器
            // 将检测后的map重新转化为json字符串
            String jsonStr = objectMapper.writeValueAsString(record);
            InputStream inputStream = new ByteArrayInputStream(jsonStr.getBytes(StandardCharsets.UTF_8));
            String fileName = goFastDfsUtil.generateFileName(filePath);
            InputStreamResource inputStreamResource = new InputStreamResource(inputStream, fileName);
            GoFastDfsUploadFileVo goFastDfsUploadFileVo = goFastDfsUtil.uploadFile(inputStreamResource);




            // 保存检测结果
            CheckResult checkResult = new CheckResult();
            checkResult.setGsid(gsid);
            checkResult.setTotalRuleNums(ruleIds.size());
            checkResult.setPassNums(passCount);
            checkResult.setRwjgnrId(tRwjgjfT.getFId());
            checkResult.setContentUrl(goFastDfsUploadFileVo.getUrl());
            checkResult.setRuleIds(ruleIdsStr);
            checkResultService.save(checkResult);


        }



    }

    @Override
    public void updateRuleById(Rule rule) {
        // 判断是否被使用了
        Set<Integer> set = new HashSet<>();
        set.add(rule.getId());
        checkIfUsed(set);

        // 修改
        updateById(rule);
    }

    /**
     * 检测文本
     *
     * @param ruleDetailVo
     * @param str
     * @return
     */
    private Boolean check(RuleDetailVo ruleDetailVo, String str) {

        // 计算条件真假 key为条件id ,value为值
        Map<Integer, String> conditionResults = evaluateConditions(ruleDetailVo, str);

        // 将规则中的content的条件id替换成ture or false
        String content = ruleDetailVo.getContent();
        String s = com.gzhu.knowledgeAdmin.common.utils.StringUtils.replaceIds(content, conditionResults);

        // 执行结果
        return extracted(s);

    }

    /**
     * 获取每个条件的真假值
     *
     * @param ruleDetailVo
     * @return
     */
    private Map<Integer, String> evaluateConditions(RuleDetailVo ruleDetailVo, String text) {
        // 记录每个条件的值
        Map<Integer, String> conditionResults = new HashMap<>();
        List<ConditionVo> conditionList = ruleDetailVo.getConditionList();

        for (ConditionVo condition : conditionList) {
            boolean result = evaluateCondition(condition, text);
            conditionResults.put(condition.getId(), result ? "true" : "false");
        }

        return conditionResults;
    }

    /**
     * 获取指定条件的真假值
     *
     * @param condition
     * @return
     */
    private boolean evaluateCondition(ConditionVo condition, String text) {

        // 记录涉及到的每个算子的值
        Map<Integer, String> operatorResults = new HashMap<>();

        List<Operator> operatorList = condition.getOperatorList();
        for (Operator operator : operatorList) {

            // 每个算子是否匹配正则表达式
            Pattern pattern = Pattern.compile(operator.getContent());
            Matcher matcher = pattern.matcher(text);
            boolean result = matcher.find();
            operatorResults.put(operator.getId(), result ? "true" : "false");
        }

        // 将条件的content中的id替换成true or false
        String content = condition.getContent();
        String s = com.gzhu.knowledgeAdmin.common.utils.StringUtils.replaceIds(content, operatorResults);

        // 调用方法进行执行结果
        return extracted(s);

    }

    // 获取逻辑关系式的值
    private boolean extracted(String s) {
        JexlBuilder jexlBuilder = new JexlBuilder();
        JexlEngine jexl = jexlBuilder.create();
        JexlExpression jexlExpression = jexl.createExpression(s);
        MapContext jexlContext = new MapContext();
        Object result = jexlExpression.evaluate(jexlContext);

        return (boolean) result;
    }


    public static String highlightMatches(String text, HashSet<String> regexHashSet) {
        for (String regex : regexHashSet) {
            text = highlight(text, regex);
        }
        return text;
    }

    private static String highlight(String text, String regex) {
        Pattern pattern = null;
        try {
            pattern = Pattern.compile(regex);
        } catch (PatternSyntaxException e) {
            System.err.println("Error in regex pattern: " + e.getMessage());
            return text;
        }

        Matcher matcher = pattern.matcher(text);
        StringBuffer sb = new StringBuffer();

        while (matcher.find()) {
            // TODO 如果是一个数据集执行多个规则，那么高亮颜色要不同
            matcher.appendReplacement(sb, "<span style=\"background-color: yellow;\">$0</span>");
        }
        matcher.appendTail(sb);

        return sb.toString();
    }

}